package cn.siriusbot.siriuspro.web.rpc;

import cn.siriusbot.siriuspro.bot.BotApi;
import cn.siriusbot.siriuspro.bot.plugin.PlugInClient;
import cn.siriusbot.siriuspro.bot.plugin.PlugInFactory;
import cn.siriusbot.siriuspro.error.MsgException;
import cn.siriusbot.siriuspro.web.R.R;
import cn.siriusbot.siriuspro.web.rpc.pojo.RpcApiBody;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@RestController
@RequestMapping("/rpc")
@Log4j2
public class RpcApiControl {

    @Autowired
    PlugInFactory plugInFactory;

    private String getName(String s) {
        return s.substring(s.lastIndexOf(".") + 1);
    }

    private Method getApiMethod(RpcApiBody body) {
        Method[] apiMethods = BotApi.class.getMethods();
        for (Method apiMethod : apiMethods) {
            String apiName = getName(apiMethod.getReturnType().getName());
            if (apiName.equals(body.getApi())) {
                return apiMethod;
            }
        }
        throw new MsgException(500, "找不到API方法!");
    }


    private Method getMethod(Object api, RpcApiBody body) {
        final Method[] methods = api.getClass().getMethods();
        for (Method method : methods) {
            if (method.getName().equals(body.getMethod())) {
                return method;
            }
        }
        throw new MsgException(500, "找不到调用方法!");
    }

    /**
     * 构建参数
     */
    private Object[] buildParams(Parameter[] parameters, RpcApiBody body) {
        try {
            if (parameters.length == 0) {
                return null;
            }
            int i = 0;
            Object[] paramObject = new Object[parameters.length];
            for (Object item : body.getParam()) {
                if (item instanceof JSONObject json) {
                    paramObject[i] = json.to(parameters[i].getParameterizedType());
                } else if (item instanceof JSONArray json) {
                    paramObject[i] = json.to(parameters[i].getParameterizedType());
                } else {
                    paramObject[i] = item;
                }
                i++;
            }
            return paramObject;
        } catch (Throwable e) {
            throw new MsgException(500, "构建参数错误");
        }
    }

    /**
     * 递归获取异常信息
     */
    private MsgException getException(Throwable e) {
        Throwable item = e;
        do {
            if (item instanceof MsgException msgException) {
                return msgException;
            }
            if (item.getCause() != null) item = item.getCause();
        } while (item.getCause() != null);
        return new MsgException(500, e.getMessage());
    }

    @SneakyThrows
    @PostMapping("/control")
    public R control(@RequestBody RpcApiBody body) {
        if (ObjectUtils.isEmpty(body.getSession())) {
            throw new MsgException(500, "构建请求错误，session为空!");
        }
        PlugInClient client = plugInFactory.getPlugInClientBySessionId(body.getSession());
        if (client == null) {
            throw new MsgException(500, "构建请求错误，session会话过期或不存在!");
        }
        final BotApi botApi = client.getBotApi();
        // 查找被调用的Api
        final Object api = getApiMethod(body).invoke(botApi);
        // 查找调用方法
        final Method method = getMethod(api, body);
        // 构建参数
        final Object[] params = buildParams(method.getParameters(), body);
        try {
            // 调用方法
            final Object result = method.invoke(api, params);
            // 构建返回值
            return new R()
                    .setData(result);
        } catch (Throwable e) {
            log.error("调用Rpc方法异常", e);
            throw getException(e);
        }
    }

}
